package com.eight.cloud.redisson.utils;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.eight.cloud.core.global.constant.NumConstant;
import com.eight.cloud.core.global.constant.StrConstant;
import com.eight.cloud.core.utils.format.ObjectFormat;
import com.eight.cloud.core.utils.format.RandomStrUtil;
import com.eight.cloud.core.utils.quick.DateUtil;
import com.eight.cloud.core.utils.quick.GenericTypeChangeUtil;
import com.eight.cloud.redisson.config.RedissonConfig;
import com.eight.cloud.redisson.model.TypedKey;
import com.fasterxml.jackson.core.type.TypeReference;
import org.redisson.api.*;
import org.redisson.client.protocol.ScoredEntry;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;

import javax.annotation.Resource;
import java.text.MessageFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Supplier;


/**
 * redis客户端工具
 *
 * @ClassName: RedisClient
 * @Author: TXC
 * @Date: 2024-10-02 09:50
 **/
@ConditionalOnMissingBean(RedisUtils.class)
@AutoConfigureAfter(RedissonConfig.class)
public class RedisUtils {
    @Resource
    private RedissonClient redissonClient;

    // 创建事务
    private RTransaction transaction;
    private final static String PAYMENT_ORDER_SERIAL = "system:serial:{0}{1}";

    /**
     * 生成流水号 流水号长度最长32位
     *
     * @param length 流水号计数位长度
     * @param index  流水号数组下标 从第几个位开始拼接到流水号中 下标从0开始
     * @param prefix 流水号前缀数组 用于redis缓存分类以及流水号前缀拼接
     * @return 流水号
     */
    public String generateSerial(int length, int index, String... prefix) {
        // 获取当天结束时间戳
        Long expireTimestamp = DateUtil.endOfDayTimestamp();
        // 获取当天日期
        String localDate = DateUtil.localDateFormat(LocalDate.now(), DateUtil.DATE_FORMAT_NO_DASH);
        // 生成缓存的key拥有获取递增的流水号
        String key = MessageFormat.format(PAYMENT_ORDER_SERIAL, localDate,
                Arrays.stream(prefix).reduce(StrConstant.EMPTY, (s1, s2) -> s1 + StrConstant.COLON + s2));
        // 获取自增后的流水号
        Long serial = getLongIncr(key, expireTimestamp);
        // 获取用于流水号的有效前缀
        String[] effectivePrefix = Math.abs(index) <= prefix.length ? Arrays.copyOfRange(prefix, (index >= NumConstant.ZERO ? index : prefix.length + index), prefix.length) : prefix;
        // 拼接前缀
        String prefixStr = Arrays.stream(effectivePrefix).reduce(StrConstant.EMPTY, (s1, s2) -> s1 + s2);
        // 返回最终流水号
        return RandomStrUtil.truncateString(prefixStr, NumConstant.CHAR_SIZE) + localDate + StrUtil.padPre(String.valueOf(serial), length, "0");
    }

    /**
     * 获取自增后的整数
     *
     * @param key 键
     * @return 自增后的整数
     */
    public Long getLongIncr(String key) {
        return redissonClient.getAtomicLong(key).incrementAndGet();
    }

    /**
     * 获取自增后的整数
     *
     * @param key       键
     * @param timestamp 过期时间
     * @return 自增后的整数
     */
    public Long getLongIncr(String key, Long timestamp) {
        RAtomicLong atomicLong = redissonClient.getAtomicLong(key);
        atomicLong.expire(Instant.ofEpochMilli(timestamp));
        return atomicLong.incrementAndGet();
    }

    /**
     * 获取自增后的浮点数
     *
     * @param key 键
     * @return 自增后的浮点数
     */
    public Double getDoubleIncr(String key) {
        return redissonClient.getAtomicDouble(key).incrementAndGet();
    }

    /**
     * 获取自增后的浮点数
     *
     * @param key       键
     * @param timestamp 过期时间
     * @return 自增后的浮点数
     */
    public Double getDoubleIncr(String key, Long timestamp) {
        RAtomicDouble atomicDouble = redissonClient.getAtomicDouble(key);
        atomicDouble.expire(Instant.ofEpochMilli(timestamp));
        return atomicDouble.incrementAndGet();
    }

    /**
     * 用于删除已存在的键,不存在的 key 会被忽略
     */
    public Long delete(String key) {
        return redissonClient.getKeys().delete(key);
    }

    /**
     * 批量删除
     */
    public Object batchDelete(String pattern) {
        return redissonClient.getKeys().deleteByPattern(pattern);
    }

    /**
     * 用于序列化给定 key ,并返回被序列化的值
     */
    public byte[] dump(String key) {
        return redissonClient.getBucket(key).dump();
    }

    /**
     * 用于检查给定 key 是否存在
     */
    public boolean exists(String key) {
        return redissonClient.getKeys().countExists(key) > NumConstant.ZERO;
    }

    /**
     * 用于设置 key 的过期时间，key 过期后将不再可用。单位以秒计
     *
     * @param seconds 过期时间-秒
     */
    public boolean expireSeconds(String key, Integer seconds) {
        return expire(key, seconds, TimeUnit.SECONDS);
    }

    /**
     * 用于设置 key 的过期时间，
     *
     * @param seconds 过期时间-秒
     */
    public boolean expire(String key, Integer seconds, TimeUnit timeUnit) {
        return redissonClient.getKeys().expire(key, seconds, timeUnit);
    }

    /**
     * 以毫秒为单位的过期日期(Unix时间戳)
     *
     * @param unixTime 毫秒时间戳：1611567518123
     */
    public boolean expireAt(String key, Long unixTime) {
        return redissonClient.getKeys().expireAt(key, unixTime);
    }

    /**
     * 以毫秒为单位设置 key 的生效时间
     *
     * @param milliseconds 毫秒
     */
    public boolean expireMilliseconds(String key, Integer milliseconds) {
        return expire(key, milliseconds, TimeUnit.MILLISECONDS);
    }


    /**
     * 用于查找所有符合给定模式 pattern 的 key
     *
     * @param pattern *
     */
    public Set<String> keys(String pattern) {
        Set<String> keys = new HashSet<>();
        redissonClient.getKeys().getKeysStreamByPattern(pattern).forEach(keys::add);
        return keys;
    }

    /**
     * 移除 key 的过期时间，key 将持久保持
     */
    public boolean clearExpire(String key) {
        return redissonClient.getKeys().clearExpire(key);
    }

    /**
     * 以毫秒为单位返回 key 的剩余过期时间
     * 返回-2 表示 key 不存在，-1 表示 key 存在但没有设置过期时间
     */
    public Long ttl(String key) {
        return redissonClient.getKeys().remainTimeToLive(key);
    }


    /**
     * 从当前数据库中随机返回一个 key
     */
    public String randomKey() {
        return redissonClient.getKeys().randomKey();
    }

    /**
     * 修改 key 的名称
     */
    public void rename(String oldKey, String newKey) {
        redissonClient.getKeys().rename(oldKey, newKey);
    }

    /**
     * 修改 key 的名称，仅当新key不存在时才会成功
     */
    public boolean renameIfAbsent(String oldKey, String newKey) {
        return redissonClient.getKeys().renamenx(oldKey, newKey);
    }

    /**
     * 用于返回 key 所储存的值的类型
     *
     * @return 返回 key 的数据类型，数据类型有: OBJECT, MAP, LIST, SET, ZSET
     */
    public RType type(String key) {
        return redissonClient.getKeys().getType(key);
    }

    /**
     * 用于设置给定 key 的值。如果 key 已经存储其他值，SET 就覆写旧值，且无视类型
     */
    public void set(String key, Object value) {
        redissonClient.getBucket(key).set(value);
    }

    public void set(String key, Object value, Long expire) {
        redissonClient.getBucket(key).set(value, expire, TimeUnit.SECONDS);
    }

    public void set(String key, Object value, Long expire, TimeUnit timeUnit) {
        redissonClient.getBucket(key).set(value, expire, timeUnit);
    }

    /**
     * 设置 key 的过期时间，单位为毫秒
     *
     * @param key    key
     * @param millis 时间戳
     */
    public void setExpireAtMillis(String key, Long millis) {
        redissonClient.getBucket(key).expire(Instant.ofEpochMilli(millis));
    }

    /**
     * 设置 key 的过期时间，单位为秒
     *
     * @param key     key
     * @param seconds 时间戳
     */
    public void setExpireAtSeconds(String key, Long seconds) {
        redissonClient.getBucket(key).expire(Instant.ofEpochSecond(seconds));
    }

    /**
     * 用于获取指定 key 的值。如果 key 不存在，返回 nil 。如果key 储存的值不是字符串类型，返回一个错误
     */
    public Object get(String key) {
        return redissonClient.getBucket(key).get();
    }

    /**
     * 用于获取指定 key 的值。如果 key 不存在，返回 nil 。如果key 储存的值不是字符串类型，返回一个错误
     */
    public <T> T get(TypedKey<T> key) {
        Object value = get(key.getKey());
        if (ObjectUtil.isNull(value)) {
            return null;
        }
        return ObjectFormat.objToObject(value, key.getType());
    }

    /**
     * 返回所有(一个或多个)给定 key 的值。 如果给定的 key 里面，有某个 key 不存在，那么这个 key 返回特殊值 nil
     */
    public List<String> batchGet(String... key) {
        RBatch batch = redissonClient.createBatch();
        Arrays.stream(key).forEach(e -> batch.getBucket(e).getAsync());
        BatchResult<?> result = batch.execute();
        return (List<String>) result.getResponses();
    }

    /**
     * 用于获取存储在指定 key 中字符串的子字符串。字符串的截取范围由 start 和 end 两个偏移量决定(包括 start 和 end 在内!)
     */
    public String getRange(String key, Integer start, Integer end) {
        return redissonClient.<String>getBucket(key).get().substring(start, end);
    }


    /**
     * 用于删除哈希表 key 中的一个或多个指定字段，不存在的字段将被忽略
     */
    public Long hashDel(String key, Object... fields) {
        return redissonClient.getMap(key).fastRemove(fields);
    }

    /**
     * 用于删除哈希表
     */
    public boolean delHash(String key) {
        return redissonClient.getMap(key).delete();
    }

    /**
     * 用于返回哈希表中指定字段的值
     */
    public Object hashGet(String key, Object field) {
        return redissonClient.getMap(key).get(field);
    }

    /**
     * 用于返回哈希表中，所有的字段和值
     */
    public Map<Object, Object> hashGetAll(String key) {
        return redissonClient.getMap(key).readAllMap();
    }

    /**
     * 用于查看哈希表的指定字段是否存在
     */
    public boolean hashExists(String key, Object field) {
        return redissonClient.getMap(key).containsKey(field);
    }

    /**
     * 用于获取哈希表中的所有域（field）
     */
    public Set<Object> hashKeys(String key) {
        return redissonClient.getMap(key).keySet();
    }

    /**
     * 返回哈希表所有域(field)的值
     */
    public List<Object> hashValues(String key) {
        return Arrays.asList(redissonClient.getMap(key).values().toArray());
    }

    /**
     * 用于获取哈希表中字段的数量
     */
    public int hashLen(String key) {
        return redissonClient.getMap(key).size();
    }

    /**
     * 用于为哈希表中的字段赋值
     */
    public Object hashSet(String key, Object field, Object value) {
        return redissonClient.getMap(key).put(field, value);
    }

    /**
     * 用于为哈希表中的字段赋值
     */
    public void hashSet(String key, Map<Object, Object> map) {
        redissonClient.getMap(key).putAll(map);
    }

    /**
     * 用于为哈希表设置过期时间
     */
    public boolean hashSetExpire(String key, Long seconds) {
        return redissonClient.getMap(key).expire(Duration.of(seconds, ChronoUnit.SECONDS));
    }

    /**
     * 用于为哈希表设置过期时间
     */
    public boolean hashSetExpireAtMillis(String key, Long millis) {
        return redissonClient.getMap(key).expire(Instant.ofEpochMilli(millis));
    }


    /**
     * 用于为哈希表中不存在的的字段赋值
     */
    public Object hashSetNx(String key, Object field, Object value) {
        return redissonClient.getMap(key).putIfAbsent(field, value);
    }


    /**
     * 用于通过索引获取列表中的元素
     *
     * @param index 0 or -1
     */
    public Object listIndex(String key, Integer index) {
        return redissonClient.getList(key).get(index);
    }

    /**
     * 用于在列表的元素后插入元素。
     *
     * @param pivot 以那个元素为中心点
     */
    public Integer listAddAfter(String key, Object pivot, Object value) {
        return redissonClient.getList(key).addAfter(pivot, value);
    }

    /**
     * 用于在列表的元素前插入元素。
     *
     * @param pivot 以那个元素为中心点
     */
    public Integer listAddBefore(String key, Object pivot, Object value) {
        return redissonClient.getList(key).addBefore(pivot, value);
    }

    /**
     * 用于返回列表的长度
     */
    public Integer listLen(String key) {
        return redissonClient.getList(key).size();
    }

    /**
     * 移出并获取列表的第一个元素
     */
    public void listPop(String key) {
        redissonClient.getList(key).fastRemove(0);
    }

    /**
     * 将一个值插入到列表头部。
     */
    public void listPush(String key, Object value) {
        redissonClient.getList(key).fastSet(0, value);
    }


    /**
     * 返回列表中指定区间内的元素，区间以偏移量 START 和 END 指定
     */
    public List<Object> listRange(String key, Integer start, Integer end) {
        return redissonClient.getList(key).readAll().subList(start, end);
    }

    /**
     * 通过索引来设置元素的值
     */
    public void listSet(String key, Integer index, Object value) {
        redissonClient.getList(key).fastSet(index, value);
    }

    /**
     * 对一个列表进行修剪(trim)，就是说，让列表只保留指定区间内的元素，不在指定区间之内的元素都将被删除
     */
    public void listTrim(String key, Integer start, Integer end) {
        redissonClient.getList(key).trim(start, end);
    }


    /**
     * 将一个或多个成员元素加入到集合中，已经存在于集合or重复的成员元素将被忽略。
     * 假如集合 key 不存在，则创建一个只包含添加的元素作成员的集合。
     * 当集合 key 不是集合类型时，返回一个错误。
     */
    public Integer setAdd(String key, Object... members) {
        return redissonClient.getSet(key).addAllCounted(Arrays.asList(members));
    }

    /**
     * 返回集合中元素的数量
     */
    public Integer setCard(String key) {
        return redissonClient.getSet(key).size();
    }


    /**
     * 判断成员元素是否是集合的成员
     */
    public Boolean setIsMember(String key, Object member) {
        return redissonClient.getSet(key).contains(member);
    }

    /**
     * 返回集合中的所有的成员。 不存在的集合 key 被视为空集合
     */
    public Set<Object> setMembers(String key) {
        return redissonClient.getSet(key).readAll();
    }

    /**
     * 命令将指定成员 member 元素从 source 集合移动到 destination 集合
     *
     * @param srcKey set1
     * @param dstKey set2
     * @param member 移动的值
     */
    public boolean setMove(String srcKey, String dstKey, Object member) {
        return redissonClient.getSet(srcKey).move(dstKey, member);
    }

    /**
     * 用于移除集合中的指定 key 的一个随机元素，移除后会返回移除的元素
     */
    public Object setPop(String key) {
        return redissonClient.getSet(key).removeRandom();
    }

    /**
     * 用于移除集合中的指定 key 的多个随机元素，移除后会返回移除的元素
     */
    public Set<Object> setPop(String key, Integer count) {
        return redissonClient.getSet(key).removeRandom(count);
    }

    /**
     * 用于返回集合中的一个随机元素。
     */
    public Object setRandMember(String key) {
        return redissonClient.getSet(key).random();
    }

    /**
     * 用于返回集合中指定count数量的随机元素
     */
    public Set<Object> setRandMember(String key, Integer count) {
        return redissonClient.getSet(key).random(count);
    }

    /**
     * 用于移除集合中的一个或多个成员元素，不存在的成员元素会被忽略
     */
    public boolean setRemove(String key, Object member) {
        return redissonClient.getSet(key).remove(member);
    }

    /**
     * 返回给定集合的并集。不存在的集合 key 被视为空集
     */
    public Set<Object> setUnion(String srcKey, String... keys) {
        return redissonClient.getSet(srcKey).readUnion(keys);
    }

    /**
     * 将给定集合的并集存储在指定的集合 destination 中。如果 destination 已经存在，则将其覆盖
     */
    public Integer setUnionStore(String dstKey, String... keys) {
        return redissonClient.getSet(dstKey).union(keys);
    }

    /**
     * 有序集合添加元素
     *
     * @param key   集合名称
     * @param value 元素
     * @param score 分数
     */
    public void scoredSortedSetAdd(String key, Object value, double score) {
        redissonClient.getScoredSortedSet(key).add(score, value);
    }

    /**
     * 删除最后一个元素
     *
     * @param key   集合名称
     */
    public Object scoredSortedSetTakeLast(String key) {
        return redissonClient.getScoredSortedSet(key).takeLast();
    }

    /**
     * 删除指定元素
     * @param key 集合名称
     * @param value 元素
     */
    public void scoredSortedSetRemove(String key, Object value) {
        redissonClient.getScoredSortedSet(key).remove(value);
    }

    /**
     * 清空集合
     * @param key 集合名称
     */
    public void scoredSortedSetClear(String key) {
        redissonClient.getScoredSortedSet(key).clear();
    }

    /**
     * 获取集合长度
     * @param key 集合名称
     * @return 集合长度
     */
    public int scoredSortedSetSize(String key) {
        return redissonClient.getScoredSortedSet(key).size();
    }

    public Collection<Object> scoredSortedSetReadAll(String key) {
        return redissonClient.getScoredSortedSet(key).readAll();
    }

    /**
     * 获取指定区间的元素
     * @param key 集合名称
     * @param start 开始位置
     * @param end 结束位置
     * @return 元素集合
     */
    public Collection<ScoredEntry<Object>> entryRangeReversed(String key, int start, int end) {
        return redissonClient.getScoredSortedSet(key).entryRangeReversed(start, end);
    }

    /**
     * 设置过期时间
     * @param key 集合名称
     * @param millis 过期时间(毫秒)
     */
    public void scoredSortedSetExpireAtMillis(String key, Long millis) {
        redissonClient.getScoredSortedSet(key).expire(Instant.ofEpochMilli(millis));
    }

    /**
     * 设置过期时间
     * @param key 集合名称
     * @param seconds 过期时间(秒)
     */
    public void scoredSortedSetExpireAtSecond(String key, Long seconds) {
        redissonClient.getScoredSortedSet(key).expire(Instant.ofEpochSecond(seconds));
    }


    /**
     * 事务提交
     */
    public void transaction(Runnable action) {
        try {
            action.run();
            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
            throw e;
        }
    }

    public RedisTransactionUtils makeTransaction() {
        transaction = redissonClient.createTransaction(TransactionOptions.defaults());
        return new RedisTransactionUtils(transaction);
    }

    /**
     * 销毁Redisson
     */
    public void destroy() {
        redissonClient.shutdown();
    }

    /**
     * 缓存或获取
     * @param key 键
     * @param supplier 供应商
     * @param timeout 超时时间
     * @param timeUnit 时间单位
     * @return 值
     */
    public <T> T cacheOrFetch(TypedKey<T> key, Supplier<T> supplier, Long timeout, TimeUnit timeUnit) {
        return cacheOrFetch(key, supplier, (value) -> set(key.getKey(), value, timeout, timeUnit));
    }

    /**
     * 缓存或获取
     * @param key 键
     * @param supplier 供应商
     * @return 值
     */
    public <T> T cacheOrFetch(TypedKey<T> key, Supplier<T> supplier, Long timeout) {
        return cacheOrFetch(key, supplier, (value) -> set(key.getKey(), value, timeout));
    }

    /**
     * 缓存或获取
     * @param key 键
     * @param supplier 供应商
     * @return 值
     */
    public <T> T cacheOrFetch(TypedKey<T> key, Supplier<T> supplier) {
        return cacheOrFetch(key, supplier, (value) -> set(key.getKey(), value));
    }

    /**
     * 缓存或获取
     * @param key 键
     * @param supplier 供应商
     * @param type 类型
     * @return 值
     */
    public <T> T cacheOrFetch(String key, Supplier<T> supplier, TypeReference<T> type) {
        return cacheOrFetch(TypedKey.<T>builder().key(key).type(type).build(), supplier);
    }

    /**
     * 缓存或获取
     * @param key 键
     * @param supplier 供应商
     * @param consumer 消费者(存入缓存)
     * @param <T> 类型
     * @return 值
     */
    private  <T> T cacheOrFetch(TypedKey<T> key, Supplier<T> supplier, Consumer<T> consumer) {
        T value = get(key);
        if (ObjectUtil.isNull(value)) {
            value = supplier.get();
            if (ObjectUtil.isNull(value)) {
                return null;
            }
            consumer.accept(value);
        }
        return value;
    }

}
